/**
 * 基于layui省市区县选择插件
 * 可指定已选和排除其他已选的省市区县三级弹框选择组件
 * 可指定层级进行选择
 * 
 * @author zhouwannian
 * @date 2021/07
 * @link https://gitee.com/zhou-wannian/layui-areaSelect.git
 */
layui.define(["jquery","layer"],function(exports){
    let VModel = null;//顶级数据模型，一切操作以此为准
    let $ = layui.jquery;
    let layer = layui.layer;
    //延时任务抽象
    let TaskCtrl = {
        taskMap:{},//任务登记(name,{code,timerId,done,clb})
        addTask:function(args){
            //同类任务存在并且code不同，需要先移出，后注册
            if(this.taskMap[args.name] && this.taskMap[args.name].code != args.code){
                let task = this.taskMap[args.name];
                if(!task.done){
                    clearTimeout(task.timerId);
                }else{
                    //直接移除dom
                    if(task.dom){
                        task.dom.parentNode.removeChild(task.dom);
                    }
                }
                //清空任务
                delete this.taskMap[args.name];
            }
            //开始任务注册
            this.taskMap[args.name] = {
                code:args.code,
                done:false,
                callback:args.callback
            }
            let newTask = this.taskMap[args.name];
            newTask.timerId = setTimeout(function(){
                newTask.dom = newTask.callback();
                newTask.done = true;
            },args.timeout);

        },
        delTask:function(args){
            if(this.taskMap[args.name] && !args.code){
                let task = this.taskMap[args.name];
                if(!task.done){
                    clearTimeout(task.timerId);
                }else{

                }
                //清空任务
                delete this.taskMap[args.name];
            }
            if(this.taskMap[args.name] && args.code){
                let task = this.taskMap[args.name];
                if(task.code == args.code && !task.done){
                    clearTimeout(task.timerId);
                    //清空任务
                    delete this.taskMap[args.name];
                }else{
                    
                }
                
            }
        }
    }
    let level2Name = {
        "0":"countries",
        "1":"provinces",
        "2":"cities",
        "3":"counties"
    };
    let name2Level = {
        "countries":0,
        "provinces":1,
        "cities":2,
        "counties":3
    };
    let area = {
        data:{
            source:null,//数据源
            //统一：国家、省份、城市、区县选择
            selected:{countries:[],provinces:[],cities:[],counties:[]},//选中回显
            expSelected:{countries:[],provinces:[],cities:[],counties:[]},//需要排除已经选中的
            level:1,//默认选择层级：省级；(可选1，2，3，分别对应省市区县三级区域)
            //selected:{countries:[],provinces:["150000","410000"],cities:["130100","430100"],counties:[]},//选中回显
            //expSelected:{countries:[],provinces:["110000"],cities:["220100"],counties:[]},//需要排除已经选中的
            //selected:{countries:[],provinces:[],cities:[],counties:[]},//选中回显
            //expSelected:{countries:["china"],provinces:[],cities:[],counties:[]},//需要排除已经选中的
            //exps:["710000","810000","820000"],//排除在外的省份(港澳台)
            exps:["710000","810000","820000"],//内置排除在外的省份(港澳台)
            url:null,//区域数据请求地址
            callback:null,//提交回调函数
            title:"请选择指定区域",//弹框标题
            floatProvince:"",//浮动省份
            timerProvince:null,//省份浮动任务ID
            floatCity:"",//浮动城市
            timerCity:null //浮动城市任务ID
        },
        root:null,
        select:function(args){
            /**
             * 参数定义：{
             *     url:省市区县完整数据源地址(必填)
             *     title:选择框标题(选填)
             *     level:区域选择范围,1表示省级，2表示市级，3表示区县级，默认1(选填)
             *     selected:当前需要回显的已经选中的省市区县，回显使用，用户操作会修改该数据(选填)
             *     expSelected:需要排除其他已选范围{province:[x,x,x],city:[x,x,x],county:[x,x,x]}，界面将隐藏该部分数据(选填)
             *     callback:点击确认按钮将回调该函数，传入selected选中数据(选填)
             *     
             *     callback执行时，将传入一个对象，包含两个属性：areaNames(字符串，选中的区域名集合),areaCodes(对象，格式同selected，选中的区域编码复合结构)
             *     
             * }
             */
            //参数与默认值进行合并
            if(args){
                this.data.url = args.url || this.data.url;
                this.data.selected = args.selected || this.data.selected;
                this.data.expSelected = args.expSelected || this.data.expSelected;
                fixData(this.data.selected);//补全下数据
                fixData(this.data.expSelected);//补全下数据
                this.data.level = args.level || this.data.level;
                this.data.callback = args.callback || this.data.callback;
                this.data.title = args.title || this.data.title;
                this.data.scope = args.scope || this.data.scope;
            }
            //将排除已选与内置排除进行组合，统一带入计算
            this.data.expSelected.provinces = this.data.expSelected.provinces.concat(this.data.exps);
            //清空任务控制器
            TaskCtrl.taskMap = {};
            //加载数据
            loadingData(this);
        }
    }

    function fixData(areaData){
        areaData.countries = areaData.countries || [];
        areaData.provinces = areaData.provinces || [];
        areaData.cities = areaData.cities || [];
        areaData.counties = areaData.counties || [];
    }

    function loadingData(instant){
        let idx = layer.load(2,{shade:[0.3,'#000']});
        $.ajax({
            url:instant.data.url,
            dataType:"json",
            success:function(resp){
                layer.close(idx);
                instant.data.source = resp.data;
                VModel = initTopVM(instant);
                initView(instant);
                bindHandle(instant);
            },
            error:function(e){
                //TODO 数据加载异常处理
                layer.msg("区域数据加载异常");
                layer.close(idx);
            }
        });
    }

    /**
     * 加工处理数据
     * 
     * @param {*} instant 
     */
    function initTopVM(instant){
        //1、构建数据源map图，格式{code:{level,index,childs,plevel,pindex,pcode,childs,scopeChilds}}
        //2、构建areaTree
        /**
         * 编制每个省市区县关联图，当前的层级、数据索引、上级的层架，上级的索引，上级代码，直接子区域数量，指定scope子区域数量
         */
        var sourceMap = {};//直接索引
        var areaTree = {//完整的区域tree
            regionCode:"china",
            regionName:"全选",
            provinces:deepObjCopy(instant.data.source)
        }

        //加入level之后，需要对原始的areaTree(三级树)进行一遍拷贝，成为指定层级树，再带入到后续的计算之中
        let levelTree = {};
        copyAreaTree2LevelTree(areaTree,levelTree,instant);
        //自顶向下递归计算节点是否为虚拟、status状态
        computeAreaTreeStatus(levelTree, instant, sourceMap);
        return {sourceMap:sourceMap,areaTree:levelTree};
    }

    /**
     * 将原始的三级树结构转为指定层级的新的树结构(递归复制法)
     * @param {*} areaTree 
     * @param {*} levelTree 
     */
    function copyAreaTree2LevelTree(rootNode, levelTree, instant){
        let childs = null;
        if(rootNode.provinces){
            rootNode.level = 0;
            childs = rootNode.provinces;
        //如果是省级    
        }else if(rootNode.cities){
            rootNode.level = 1;
            childs = rootNode.cities;
        //如果是市级    
        }else if(rootNode.counties){
            rootNode.level = 2;
            childs = rootNode.counties;
        //如果是区县级    
        }else{
            rootNode.level = 3;    
        }
        
        //层级判断，如果当前层级不超过，加入到新的树中
        if(rootNode.level <= instant.data.level){
            //基础属性的复制
            levelTree.regionCode = rootNode.regionCode;
            levelTree.regionName = rootNode.regionName;
            levelTree.level = rootNode.level;
            //决定是否需要有子节点
            if(rootNode.level < instant.data.level && childs){
                levelTree.childs = [];//给定空的数组，带入到下次计算中使用
            }
        }

        //如果层级小于目标层级并且包含子节点，那么需要递归到下一级中
        if(rootNode.level < instant.data.level && childs){
            
            for (let index = 0; index < childs.length; index++) {
                let childNode = childs[index];
                levelTree.childs[index] = {};
                //递归带入计算
                copyAreaTree2LevelTree(childNode,levelTree.childs[index],instant);
            }
        }
    }

    //深度拷贝，不直接破坏元对象
    function deepObjCopy(jsonObj){
        return JSON.parse(JSON.stringify(jsonObj));
    }

    /**
     * 递归计算区域数节点：虚拟节点、子节点、scope节点数量
     * @param {*} rootNode 
     */
    function computeAreaTreeStatus(rootNode, instant, sourceMap){
        
        //用来对比的虚拟节点数组(即expSelected)
        let compareVirtuallyArray = null;
        //真实已选的节点数组(即selected)
        let compareRealityArray = null;
        //如果是全部顶级
        if(rootNode.level == 0){
            compareVirtuallyArray = instant.data.expSelected.countries;
            compareRealityArray = instant.data.selected.countries;
        //如果是省级    
        }else if(rootNode.level == 1){
            compareVirtuallyArray = instant.data.expSelected.provinces;
            compareRealityArray = instant.data.selected.provinces;
        //如果是市级    
        }else if(rootNode.level == 2){
            compareVirtuallyArray = instant.data.expSelected.cities;
            compareRealityArray = instant.data.selected.cities;
        //如果是区县级    
        }else{
            compareVirtuallyArray = instant.data.expSelected.counties;
            compareRealityArray = instant.data.selected.counties;
        }
        //计算是否为虚拟节点
        if(compareVirtuallyArray && compareVirtuallyArray.indexOf(rootNode.regionCode) > -1){
            rootNode.type = 0
        }else{
            rootNode.type = 1;
        }
        //计算是否为已选节点
        if(compareRealityArray && compareRealityArray.indexOf(rootNode.regionCode) > -1){
            rootNode.realStatus = "checked";
            rootNode.viewStatus = "checked";
        }

        //当前node不包含status属性时，进行默认赋值,均默认为未选中状态
        rootNode.viewStatus = rootNode.viewStatus || "unchecked";
        rootNode.realStatus = rootNode.realStatus || "unchecked";
        //求解已选子节点的数量(最末级的已选可见节点数量设1，并且不展示)
        if(rootNode.type && rootNode.realStatus == "checked" && !rootNode.childs){
            rootNode.viewNum = 1;
            rootNode.viewNumShow = false;
        }

        //虚拟节点的子节点不需要递归计算了
        if(rootNode.type && rootNode.childs){
            let allChildNum = rootNode.childs.length;//总的子节点数量
            let virtuallyChildNum = 0;//虚拟子节点数量
            let realityChidNum = 0;//真实子节点数量
            let checkedRealityNum = 0;//已选真实子节点数量
            let checkedViewNum = 0;//已选视图子节点数量
            let indViewNum = 0;//半选视图子节点数量
            for (let index = 0; index < rootNode.childs.length; index++) {
                let childNode = rootNode.childs[index];
                childNode.parent = rootNode;//维持引用关系
                //重点计算：父节点的realStatus=checked时需要传递给子节点
                if(rootNode.realStatus == "checked"){
                    childNode.realStatus = "checked";
                    childNode.viewStatus = "checked";
                }
                //递归带入计算
                computeAreaTreeStatus(childNode,instant, sourceMap);

                //累加已选的子节点数量
                if(childNode.viewNum){
                    if(!rootNode.viewNum){
                        rootNode.viewNum = 0;
                        rootNode.viewNumShow = true;
                    }
                    rootNode.viewNum += childNode.viewNum;
                }

                if(childNode.type == 0){
                    virtuallyChildNum += 1;
                }else{
                    realityChidNum += 1;
                    if(childNode.realStatus == "checked"){
                        checkedRealityNum += 1;
                    }
                }
                if(childNode.viewStatus == "checked"){
                    checkedViewNum += 1;
                }
                if(childNode.viewStatus == "indeterminate"){
                    indViewNum += 1;
                }
            }
            //计算节点的realStatus
            if(virtuallyChildNum == 0 && allChildNum == checkedRealityNum){
                rootNode.realStatus = "checked";
            }
            //计算节点的viewStatus
            if(checkedViewNum == realityChidNum){
                rootNode.viewStatus = "checked";
            }else{
                if(checkedViewNum > 0){
                    rootNode.viewStatus = "indeterminate";
                }else{
                    if(indViewNum > 0){
                        rootNode.viewStatus = "indeterminate";
                    }
                }
            }
            //计算节点是否包含了虚拟节点
            if(virtuallyChildNum > 0){
                rootNode.containVirtaulNode = true;
            }
            //如果所有子节点都是虚拟节点，那么父节点也需要设置为虚拟节点
            if(virtuallyChildNum == allChildNum){
                rootNode.type = 0;
            }
            
        }
        sourceMap[rootNode.regionCode] = rootNode;
    }

    /**
     * 根据根节点编码查找下级可用节点树
     * 递归计算
     * 输出格式同：instant.data.selected
     * @param {*} rootNode 
     */
     function searchAreaTree2Select(nodeCode,rltMap){
        //递归处理china
        let node = VModel.sourceMap[nodeCode];
        if(node){
            if(node.containVirtaulNode && node.childs){
                for (let index = 0; index < node.childs.length; index++) {
                    let child = node.childs[index];
                    if(child.type){
                        searchAreaTree2Select(child.regionCode,rltMap);
                    }
                }
            }else{
                if(!rltMap[level2Name[node.level]]){
                    rltMap[level2Name[node.level]] = [];
                }
                rltMap[level2Name[node.level]].push(nodeCode);
            }
        }

        return rltMap;
    }
    
    //视图初始化
    function initView(instant){
        instant.root = document.createElement("div");//弹框根节点
        instant.root.className = "com-area-dialog";
        let html = [];
        
        //顶部
        html.push(`<div class="com-area-box">`);
        html.push(`<div class="com-area-box-header">`);
        html.push(`<div class="com-area-box-header-title">${instant.data.title}</div>`);
        html.push(`<div class="com-area-box-btn-close"><i class="layui-icon layui-icon-close"></i></div>`);
        html.push(`</div>`);

        //中间      
        html.push(`<div class="com-area-box-body">`);
        if(VModel.areaTree.type){
            initProvince(instant,html);
        }else{
            initNothing(instant,html);
        }
        html.push(`</div>`);

        //底部(操作)
        if(VModel.areaTree.type){
            let rootView = node2ViewStatic(VModel.areaTree,false);
            html.push(`<div class="com-area-box-opt">`);
            html.push(`<div class="com-area-box-opt-allcheck"><span class="com-area-body-item-checkspan ${rootView.viewStatus}" id="com-area-viewstatus-china"></span><input type="checkbox" v-code="china" ${rootView.checkBoxStatus}/>全选</div>`);
            html.push(`<div><span >已选择<label id="com-area-viewnum-china">${rootView.viewNum}</label>个区域</span><button class="com-area-box-opt-btn ${rootView.subBtnDisabledClass}" ${rootView.subBtnDisabled} type="button">提交</button></div>`);
            html.push(`</div>`);
        }else{
            //根节点空白
        }
        
        html.push(`</div>`);

        instant.root.innerHTML = html.join("");

        document.body.append(instant.root);
        
    }

    function initNothing(instant, html){
        html.push(`<h3 class="com-area-body-noarea">没有任何可选区域了<h3>`);
    }

    //初始化省份
    function initProvince(instant,html){
        let source = VModel.areaTree.childs;
        //检查是否需要展示float图标
        let floatDownClass = VModel.areaTree.level < (instant.data.level - 1) ? "" : "com-area-body-item-float-hidden";
        for (let index = 0; index < source.length; index++) {
            const province = source[index];
            //只渲染非虚拟节点
            if(province.type){

                let nodeView = node2ViewStatic(province,true);

                html.push(`<span class="com-area-body-item">`);
                html.push(`<span class="com-area-body-item-checkspan ${nodeView.viewStatus}" id="com-area-viewstatus-${province.regionCode}"></span>`);
                html.push(`<input type="checkbox" v-code="${province.regionCode}" ${nodeView.checkBoxStatus}/>`);

                html.push(`<div class='com-area-body-item-view' v-code="${province.regionCode}">`);
                html.push(`<label>${province.regionName}</label>`);
                html.push(`<label class="com-area-body-item-num" id="com-area-viewnum-${province.regionCode}">${nodeView.viewNum}</label>`);
                html.push(`<i class="layui-icon layui-icon-down ${floatDownClass}"></i>`);
                html.push(`</div>`);

                html.push(`</span>`);
            }
        }
    }

    /**
     * 节点到视图的静态转换
     * 
     * @param {*} node 
     */
    function node2ViewStatic(node,numWrap){
        let rlt = {};
        rlt.viewNum = node.viewNumShow?node.viewNum:"";
        if(numWrap){
            rlt.viewNum = rlt.viewNum==""?"":("("+ rlt.viewNum +")")
        }
        //根节点特殊处理下
        if(node.level == 0){
            rlt.viewNum = rlt.viewNum==""?0:rlt.viewNum;
        }
        rlt.viewStatus = node.viewStatus != "unchecked" ? ` com-area-body-item-${node.viewStatus}` : "";
        rlt.checkBoxStatus = node.viewStatus == "checked" ? `checked="true"` : "";

        //针对提交按钮特别处理下
        if(node.level == 0){
            if(node.viewNum > 0){
                rlt.subBtnDisabled = "";
                rlt.subBtnDisabledClass = "";
            }else{
                rlt.subBtnDisabled = "disabled";
                rlt.subBtnDisabledClass = "com-area-box-opt-disabled";
            }
            
        }

        return rlt;
    }

    /**
     * 节点到视图的动态转换
     * 
     * @param {*} node 
     */
    function node2ViewDynamic(node, instant){
        
        let rlt = {};
        if(node.level == 0){
            rlt.viewNum = node.viewNumShow?node.viewNum:"0";
        }else{
            rlt.viewNum = node.viewNumShow ?("("+ node.viewNum +")"):""
        }
        rlt.viewStatus = node.viewStatus != "unchecked" ? ` com-area-body-item-${node.viewStatus}` : "";
        rlt.checkBoxStatus = node.viewStatus == "checked" ? true : false;
        let rcode = node.regionCode;
        let viewStatus = instant.root.querySelector("#com-area-viewstatus-"+rcode);
        if(viewStatus){
            //更新选中状态
            viewStatus.className = `com-area-body-item-checkspan ${rlt.viewStatus}`;
            //更新子节点数
            instant.root.querySelector("#com-area-viewnum-"+rcode).innerHTML = rlt.viewNum;
            //更新checkbox选中情况
            instant.root.querySelector(`input[v-code="${rcode}"`).checked = rlt.checkBoxStatus;
        }
    }

    
    //事件绑定处理(事件委托模式)
    function bindHandle(instant){
        
        //绑定所有checkbox change事件
        instant.root.onchange = function(e){
            handleUserCheck(e,instant);
        }

        //绑定所有浮动选择事件(mouseover|mouseout)
        let hoverItems = instant.root.querySelectorAll("div.com-area-body-item-view");
        for (let index = 0; index < hoverItems.length; index++) {
            //检查level是否允许float
            let code = hoverItems[index].getAttribute("v-code");
            if(VModel.sourceMap[code].level < instant.data.level){
                //只有当当前节点的层级小于目标层级才可能进行floatview绑定
                bindAreaItemHover(hoverItems[index], instant)
            }
        }

        //绑定关闭弹框
        instant.root.querySelector(".com-area-box-btn-close").onclick = function(e){
            handleUserClose(e,instant);
        }

        //绑定提交按钮
        instant.root.querySelector("button").onclick = function(e){
            handleUserSubmit(e,instant);
        }
    }

    //绑定区域元素的hover模拟事件
    function bindAreaItemHover(areaDom, instant){
        areaDom.onmouseenter = function(e){
            //鼠标移入，触发子区域选择
            let code = e.srcElement.getAttribute("v-code");
            TaskCtrl.delTask({
                name:"drop-floatview-"+VModel.sourceMap[code].level,
                code:code,
                timeout:100,
            });
            TaskCtrl.addTask({
                name:"create-floatview-"+VModel.sourceMap[code].level,
                code:code,
                timeout:500,
                callback:(function(code,src,instant){
                    let icode = code;
                    let isrc=src;
                    let ins=instant;
                    let fn = function(){
                        return triggleChildNodeSelect(icode,isrc, ins);
                    }
                    return fn;
                })(code,e.srcElement,instant)
            });
            
        };
        areaDom.onmouseleave = function(e){
            //注意：当前区域的移除，只需要关闭对应级别的floatview
            //注意：
            let code = e.srcElement.getAttribute("v-code");
            TaskCtrl.delTask({
                name:"create-floatview-"+VModel.sourceMap[code].level,
                code:code,
                timeout:100,
            });
            TaskCtrl.addTask({
                name:"drop-floatview-"+VModel.sourceMap[code].level,
                code:code,
                timeout:100,
                callback:(function(code,src,instant){
                    let icode = code;
                    let isrc=src;
                    let ins=instant;
                    let fn = function(){
                        return triggleChildNodeDrop(icode,isrc, ins);
                    }
                    return fn;
                })(code,e.srcElement,instant)
            });
        }
    }

    /**
     * 当前区域移除，关闭对应浮动框
     * @param {*} code 
     * @param {*} src 
     * @param {*} instant 
     */
    function triggleChildNodeDrop(code,src, instant){
        let node = VModel.sourceMap[code];
        //区县级别移除不需要考虑
        if(node.level != 3){
            dropFloatView(node.level,instant);
        }
    }

    /**
     * 触发子节点选择
     * 
     * @param {*} code 
     * @param {*} instant 
     */
    function triggleChildNodeSelect(code,src, instant){
        //关闭对应级别浮动框，并显示当前级别浮动框
        let node = VModel.sourceMap[code];
        //目前只有province和city级别才需要浮动，区县不需要处理
        if(node.level != 3){
            dropFloatView(node.level,instant);
            buildFloatView(node,src,instant);
        }
    }

    /**
     * 用户关闭选择框
     * 
     * @param {*} event 
     * @param {*} instant 
     */
    function handleUserClose(event,instant){
        document.body.removeChild(instant.root);
    }

    /**
     * 用户提交选中
     * 
     * @param {*} event 
     * @param {*} instant 
     */
    function handleUserSubmit(event,instant){
        let rlt = getSelectedResult(instant);
        if(instant.data.callback){
            instant.data.callback(rlt);
        }
        handleUserClose(event,instant);
    }

    /**
     * 获取选中的结果
     */
    function getSelectedResult(instant){
        let areaCodes = {};
        computeAreaTreeSelected(VModel.areaTree,areaCodes);
        areaNameArray = [];
        for(let key in areaCodes){
            let array = areaCodes[key];
            for(let index=0;index<array.length;index++){
                areaNameArray.push(VModel.sourceMap[array[index]].regionName);
            }
        }
        return {
            areaCodes:areaCodes,
            areaNames:areaNameArray.join(",")
        }
    }

    /**
     * 递归计算区域树选中结果
     * 
     * @param {*} rootNode 
     * @param {*} rltMap 
     */
    function computeAreaTreeSelected(rootNode,rltMap){
        if(rootNode.type){
            if(rootNode.realStatus == "checked" && !rootNode.containVirtaulNode){
                if(!rltMap[level2Name[rootNode.level]]){
                    rltMap[level2Name[rootNode.level]] = [];
                }
                rltMap[level2Name[rootNode.level]].push(rootNode.regionCode);
            }else{
                let childs = rootNode.childs;
                if(childs){
                    for (let index = 0; index < childs.length; index++) {
                        computeAreaTreeSelected(childs[index],rltMap);
                    }
                }
            }
        }
    }

    /**
     * 用户选中操作处理
     * 
     * @param {*} event 
     * @param {*} instant 
     */
    function handleUserCheck(event,instant){
        let areaInput = event.srcElement;
        triggleNodeChange(areaInput.getAttribute("v-code"), areaInput.checked, instant);
    }

    /**
     * 触发节点选择变化
     * @param {*} nodeCode 节点编码
     * @param {*} select 是否选中
     */
    function triggleNodeChange(nodeCode, select, instant){
        //对节点重新赋值checkstatus，然后重新递归计算整个tree
        refreshTreeMapNodes(VModel.sourceMap[nodeCode],select);
        //重新计算树节点数据
        recomputeTreeMap(VModel.areaTree);
        //映射视图更新
        refreshView(nodeCode,instant);
    }

    /**
     * 重新计算树节点关键属性值
     * 
     * @param {*} rootNode 
     */
    function recomputeTreeMap(rootNode){
        let childs = rootNode.childs;
        //求解已选子节点的数量(最末级的已选可见节点数量设1，并且不展示，其他节点递归累加)
        if(rootNode.type && rootNode.realStatus == "checked" && !childs){
            rootNode.viewNum = 1;
            rootNode.viewNumShow = false;
        }else{
            rootNode.viewNum = 0;
            rootNode.viewNumShow = false;
        }

        //虚拟节点的子节点不需要递归计算了
        if(rootNode.type && childs){
            let allChildNum = childs.length;//总的子节点数量
            let virtuallyChildNum = 0;//虚拟子节点数量
            let realityChidNum = 0;//真实子节点数量
            let checkedRealityNum = 0;//已选真实子节点数量
            let checkedViewNum = 0;//已选视图子节点数量
            let indViewNum = 0;//半选视图子节点数量
            for (let index = 0; index < childs.length; index++) {
                let childNode = childs[index];
                //递归带入计算
                recomputeTreeMap(childNode);

                //累加已选的子节点数量
                if(childNode.viewNum){
                    if(!rootNode.viewNum){
                        rootNode.viewNum = 0;
                        rootNode.viewNumShow = true;
                    }
                    rootNode.viewNum += childNode.viewNum;
                }

                if(childNode.type == 0){
                    virtuallyChildNum += 1;
                }else{
                    realityChidNum += 1;
                    if(childNode.realStatus == "checked"){
                        checkedRealityNum += 1;
                    }
                }
                if(childNode.viewStatus == "checked"){
                    checkedViewNum += 1;
                }
                if(childNode.viewStatus == "indeterminate"){
                    indViewNum += 1;
                }
            }
            //计算节点的realStatus
            if(virtuallyChildNum == 0 && allChildNum == checkedRealityNum){
                rootNode.realStatus = "checked";
            }else{
                rootNode.realStatus = "unchecked";
            }
            //计算节点的viewStatus
            if(checkedViewNum == realityChidNum){
                rootNode.viewStatus = "checked";
            }else{
                if(checkedViewNum > 0){
                    rootNode.viewStatus = "indeterminate";
                }else{
                    if(indViewNum > 0){
                        rootNode.viewStatus = "indeterminate";
                    }else{
                        rootNode.viewStatus = "unchecked";
                    }
                }
            }
            //计算节点是否包含了虚拟节点
            if(virtuallyChildNum > 0){
                rootNode.containVirtaulNode = true;
            }
        }
    }

    /**
     * 根据所选树节点数据和选中状态，批量更新对应节点状态
     * 
     * @param {*} treeData 
     * @param {*} select 
     */
    function refreshTreeMapNodes(rootNode,select){
        let status = select? "checked":"unchecked";
        //虚拟节点直接跳出
        if(rootNode.type){
            if(rootNode.containVirtaulNode){
                //如果当前节点包含虚拟节点
                rootNode.viewStatus = status;
                //rootNode.realStatus = "";
            }else{
                rootNode.viewStatus = status;
                rootNode.realStatus = status;
            }
            if(rootNode.childs){
                let childs = rootNode.childs;
                for (let index = 0; index < childs.length; index++) {
                    refreshTreeMapNodes(childs[index],select);
                }
            }
        }
    }

    /**
     * 更新以当前节点为起点的视图
     * @param {*} nodeCode 
     * @param {*} instant 
     */
    function refreshView(nodeCode, instant){
        //向子节点递归
        refreshChildView(nodeCode,instant);
        //向父节点递归
        refreshParentView(nodeCode,instant);
        //计算提交按钮是否可用
        refreshSubmitBtnStatus(instant);
    }

    function refreshSubmitBtnStatus(instant){
        let subBtn = instant.root.querySelector("button");
        if(VModel.areaTree.viewNum > 0){
            subBtn.className = "com-area-box-opt-btn";
            subBtn.disabled = false;
        }else{
            subBtn.className = "com-area-box-opt-btn com-area-box-opt-disabled";
            subBtn.disabled = true;
        }
    }

    function refreshChildView(nodeCode,instant){
        let node = VModel.sourceMap[nodeCode];
        if(node.type){
            //当前node更新
            node2ViewDynamic(node,instant);
            if(node.childs){
                for (let index = 0; index < node.childs.length; index++) {
                    const child = node.childs[index];
                    refreshChildView(child.regionCode,instant);
                }
            }
        }
    }

    function refreshParentView(nodeCode,instant){
        if(VModel.sourceMap[nodeCode].parent){
            let pnode = VModel.sourceMap[nodeCode].parent;
            if(pnode.type){
                node2ViewDynamic(pnode,instant);
                refreshParentView(pnode.regionCode,instant);
            }
        }
    }

    /**
     * 移除已经存在的浮动框
     * 
     * @param {*} level 
     * @param {*} instant 
     */
    function dropFloatView(level,instant){
        var className = (level == 1? ".com-area-float-city":".com-area-float-county");
        let floats = instant.root.querySelectorAll(className);
        
        for (let index = 0; index < floats.length; index++) {
            instant.root.removeChild(floats[index]);
        }

        if(level == 1){
            instant.data.floatProvince = null;
            instant.data.timerProvince = null;
        }else{
            instant.data.floatCity = null;
            instant.data.timerCity = null;
        }
    }

    /**
     * 绑定浮动视图处理
     * @param {*} floatDom 
     * @param {*} code 
     * @param {*} instant 
     */
    function bindFloatviewHandle(floatDom, code, instant){
        //移入
        floatDom.onmouseenter = function(e){
            //取消关闭任务
            TaskCtrl.delTask({name:"drop-floatview-"+VModel.sourceMap[code].level});  
            if(VModel.sourceMap[code].level == 2){
                TaskCtrl.delTask({name:"drop-floatview-1"}); 
            }          
        }
        //移出
        floatDom.onmouseleave = function(e){
            //开始关闭任务
            TaskCtrl.addTask({
                name:"drop-floatview-"+VModel.sourceMap[code].level,
                code:code,
                timeout:100,
                callback:(function(code,src,instant){
                    let icode = code;
                    let isrc=src;
                    let ins=instant;
                    let fn = function(){
                        return triggleChildNodeDrop(icode,isrc, ins);
                    }
                    return fn;
                })(code,e.srcElement,instant)
            });
        }
    }

    /**
     * 构建浮动区域选择
     * 
     * @param {*} vm 
     * @param {*} src 
     * @param {*} instant 
     */
    function buildFloatView(node,src,instant){
        
        let ele = document.createElement("div");
        let html = [];
        if(node.level == 1){
            ele.className = "com-area-float-city";
            ele.setAttribute("v-float","1");
        }else{
            ele.className = "com-area-float-county";
            ele.setAttribute("v-float","2");
        }
        html.push(`<div class="com-area-float-box" v-float="${node.level}">`);
        //检查是否需要展示floatview图标
        let floatDownClass = node.level < (instant.data.level - 1) ? "" : "com-area-body-item-float-hidden";
        for (let index = 0; index < node.childs.length; index++) {
            const item = node.childs[index];
            if(item.type){
                let view = node2ViewStatic(item,true);
                html.push(`<span class="com-area-body-item">`);
                html.push(`<span class="com-area-body-item-checkspan ${view.viewStatus}" id="com-area-viewstatus-${item.regionCode}"></span>`);
                html.push(`<input type="checkbox" v-code="${item.regionCode}" ${view.checkBoxStatus}/>`);
                html.push(`<div class='com-area-body-item-view' v-code="${item.regionCode}">`);
                html.push(`<label>${item.regionName}</label>`);
                html.push(`<label class="com-area-body-item-num" id="com-area-viewnum-${item.regionCode}">${view.viewNum}</label>`);
                html.push(`<i class="layui-icon layui-icon-right ${floatDownClass}"></i>`);
                html.push(`</div>`);
                html.push(`</span>`);
            }
        }
        html.push(`</div>`);

        ele.innerHTML = html.join("");
        
        instant.root.append(ele);

        //定位
        computeFloatPosition(src,ele,instant,node.level);

        //继续绑定事件
        let hoverItems = ele.querySelectorAll("div.com-area-body-item-view");
        for (let index = 0; index < hoverItems.length; index++) {
            //只有层级小于目标层级才允许进行float绑定
            let code = hoverItems[index].getAttribute("v-code");
            if(VModel.sourceMap[code].level < instant.data.level){
                bindAreaItemHover(hoverItems[index], instant)
            }
        }

        //绑定floatview移入移除事件处理
        bindFloatviewHandle(ele,node.regionCode,instant);

        return ele;
    }

    /**
     * 计算浮动框最终显示位置
     * 
     * @param {*} src 参照元素
     * @param {*} target 目标浮动框
     * @param {*} instant 
     * @param {*} level 层级
     */
    function computeFloatPosition(src,target,instant,level){
        let winPos = instant.root.getBoundingClientRect();
        let srcPos = src.getBoundingClientRect();
        let targetPos = target.getBoundingClientRect();
        let maxHeight = targetPos.height + 130;//两级浮动框最大高度
        if(level==1){
            //上下浮动框
            //确定浮动框的浮动位置：下方或者上方
            let direction = "";
            if((srcPos.top+maxHeight) >= winPos.height){
                direction = "top";
            }else{
                direction = "bottom";
            }
            target.style.left = (srcPos.left - ((targetPos.width-srcPos.width)/2))+"px";
            target.style.top = direction=="top"?((srcPos.top-targetPos.height)+"px"):((srcPos.top+srcPos.height)+"px")
        }else{
            //右侧浮动框
            target.style.left = (srcPos.left+srcPos.width)+"px";
            target.style.top = (srcPos.top - (targetPos.height/2))+"px";
        }
    }

    exports("area",area);
});